package cn.yangliu.tools;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.TrustStrategy;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.entity.mime.HttpMultipartMode;
import org.apache.http.entity.mime.MultipartEntityBuilder;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.ssl.SSLContextBuilder;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.StringUtils;

import javax.net.ssl.SSLContext;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;

/**
 * Created by Melody
 * on 2017/4/5.
 */
public class CommClient {
    public static final String UTF8 = "UTF-8";
    public static final String GBK = "GBK";
    public static final String GB2312 = "GB2312";
    private static final Logger logger = LoggerFactory.getLogger(CommClient.class);

    public static String sendGetRequest(String reqURL, String decodeCharset, Map<String, Object> params, Map<String, String> headers) {
        return invokeGet(reqURL, decodeCharset, params, headers);
    }

    public static String sendGetRequest(String reqURL, Map<String, Object> params, Map<String, String> headers) {
        return invokeGet(reqURL, UTF8, params, headers);
    }

    public static String sendGetRequest(String reqURL, String decodeCharset, Map<String, Object> params) {
        return invokeGet(reqURL, decodeCharset, params, null);
    }

    public static String sendGetRequest(String reqURL) {
        return invokeGet(reqURL, UTF8, null, null);
    }

    public static String sendGetRequest(String reqURL, Map<String, Object> params) {
        return invokeGet(reqURL, UTF8, params, null);
    }

    public static String sendPostRequest(String reqURL, String decodeCharset, Map<String, Object> params, Map<String, String> headers) {
        return invokePost(reqURL, decodeCharset, params, headers);
    }

    public static String sendPostRequest(String reqURL, Map<String, Object> params, Map<String, String> headers) {
        return invokePost(reqURL, UTF8, params, headers);
    }

    public static String sendPostRequest(String reqURL, String decodeCharset, Map<String, Object> params) {
        return invokePost(reqURL, decodeCharset, params, null);
    }

    public static String sendPostRequest(String reqURL, Map<String, Object> params) {
        return invokePost(reqURL, UTF8, params, null);
    }

    public static String sendPostRequest(String reqURL) {
        return invokePost(reqURL, UTF8, null, null);
    }

    private static String invokeGet(String reqURL, String decodeCharset, Map<String, Object> params, Map<String, String> headers) {
        logger.info("请求地址:[" + reqURL + "]参数:" + params);
        if (params != null) {
            reqURL = setParams(reqURL, params);
        }
        HttpGet httpGet = new HttpGet(reqURL);
        return doRequest(reqURL, decodeCharset, httpGet, headers);
    }

    private static String invokePost(String reqURL, String decodeCharset, Map<String, Object> params, Map<String, String> headers) {
        logger.info("请求地址:[" + reqURL + "]参数:" + params);
        if (Objects.nonNull(headers)) {
            logger.info("请求头:" + headers);
        }
        // 创建org.apache.http.client.methods.HttpGet
        HttpPost httpPost = postForm(reqURL, params);
        return doRequest(reqURL, decodeCharset, httpPost, headers);
    }

    private static <T extends HttpRequestBase> T addHeaders(T requestBase, Map<String, String> headers) {
        if (Objects.isNull(headers)) {
            return requestBase;
        }
        Set<String> headerNames = headers.keySet();
        headerNames.forEach(name -> {
            String value = headers.get(name);
            if (!StringUtils.isEmpty(name) && !StringUtils.isEmpty(value)) {
                requestBase.addHeader(name, value);
            }
        });
        return requestBase;
    }

    public static String invokeGet(String reqURL, HttpGet httpGet) {
        return doRequest(reqURL, UTF8, httpGet, null);
    }

    public static String invokePost(String reqURL, HttpPost httpPost) {
        return doRequest(reqURL, UTF8, httpPost, null);
    }

    private static String doRequest(String reqURL, String decodeCharset, HttpRequestBase request, Map<String, String> headers) {
        // 响应内容
        String responseContent = null;
        // 创建默认的httpClient实例
        CloseableHttpClient httpClient = createSSLClientDefault(reqURL);

        addHeaders(request, headers);
        responseContent = getResponseContent(reqURL, decodeCharset, request, httpClient);
        return responseContent;
    }

    public static String getResponseContent(String reqURL, String decodeCharset, HttpRequestBase request, CloseableHttpClient httpClient) {
        String responseContent = null;
        try {

            // 获取响应实体
            HttpEntity entity = getResponseEntity(reqURL, decodeCharset, request, httpClient);
            if (null != entity) {
                responseContent = EntityUtils.toString(entity,
                        decodeCharset == null ? UTF8 : decodeCharset);
                EntityUtils.consume(entity);
            }
            if (StringUtils.isEmpty(responseContent)) {
                logger.info(String.format("接口请求错误===>url:%s", reqURL));
            } else {
                responseContent = ascii2native(responseContent);
                logger.info(String.format("接口请求成功===>url:%s; response%s", reqURL, responseContent));
            }
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
            throw new RuntimeException(e.getMessage(), e);
        } finally {
            try {
                httpClient.close(); // 关闭连接,释放资源
            } catch (Exception e) {
                throw new RuntimeException(e.getMessage(), e);
            }
        }
        return responseContent;
    }

    public static HttpEntity getResponseEntity(String reqURL, String decodeCharset, HttpRequestBase request, CloseableHttpClient httpClient) {
        String responseContent = null;
        HttpEntity entity = null;
        try {
            // 执行请求
            HttpResponse response = httpClient.execute(request);
            // 获取响应实体
            entity = response.getEntity();
            if (response.getStatusLine().getStatusCode() != 200) {
                responseContent = EntityUtils.toString(entity,
                        decodeCharset == null ? UTF8 : decodeCharset);
                EntityUtils.consume(entity);
                logger.info(String.format("接口请求错误===>url:%s; response%s", reqURL, responseContent));
            }
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
            throw new RuntimeException(e.getMessage(), e);
        }
        return entity;
    }

    public static InputStream getResponseStream(String reqURL, String decodeCharset, HttpRequestBase request, CloseableHttpClient httpClient) {
        HttpEntity entity = null;
        // 获取响应实体
        entity = getResponseEntity(reqURL, decodeCharset, request, httpClient);
        try {
            return entity.getContent();
        } catch (Exception e) {
            throw new RuntimeException(e.getMessage(), e);
        }
    }


    public static String setParams(String url, Map<String, Object> params) {
        if (Objects.isNull(params)) {
            return url;
        }
        StringBuilder sb = new StringBuilder(url).append("?");

        Set<String> keys = params.keySet();
        for (String key : keys) {
            Object value = params.get(key);
            if (value instanceof String) {
                sb.append(key).append("=").append(value.toString()).append("&");
                continue;
            }

            if (value instanceof Object[]) {
                Object[] values = (Object[]) value;
                if (values.length == 0) {
                    continue;
                }

                for (Object val : values) {
                    sb.append(key).append("=").append(val.toString()).append("&");
                }
                continue;
            }
            if (value instanceof List) {
                List<?> values = (List<?>) value;
                if (values.size() == 0) {
                    continue;
                }
                for (Object v : values) {
                    sb.append(key).append("=").append(v.toString()).append("&");
                }
                continue;
            }
            sb.append(key).append("=").append(value.toString()).append("&");

        }
        return sb.toString();
    }

    public static HttpPost postForm(String url, Map<String, Object> params) {
        HttpPost httPost = new HttpPost(url);
        if (params != null) {
            List<NameValuePair> nvps = new ArrayList<>();
            Set<String> keySet = params.keySet();
            for (String key : keySet) {
                Object value = params.get(key);
                if (value instanceof String) {
                    nvps.add(new BasicNameValuePair(key, value.toString()));
                } else if (value instanceof Object[]) {
                    Object[] values = (Object[]) value;
                    if (values.length != 0) {
                        for (Object val : values) {
                            nvps.add(new BasicNameValuePair(key, val.toString()));
                        }
                    }
                } else if (value instanceof List) {
                    List<?> values = (List<?>) value;
                    if (values.size() > 0) {
                        for (Object v : values) {
                            nvps.add(new BasicNameValuePair(key, v.toString()));
                        }
                    }
                } else {
                    nvps.add(new BasicNameValuePair(key, value.toString()));
                }

            }
            try {
                httPost.setEntity(new UrlEncodedFormEntity(nvps, UTF8));
            } catch (Exception e) {
                throw new RuntimeException(e.getMessage(), e);
            }
        }
        return httPost;
    }


    private static final String SSL_PREFIX = "https://";

    public static CloseableHttpClient createSSLClientDefault(String url) {
        if (url.startsWith(SSL_PREFIX)) {
            try {
                //信任所有
                SSLContext sslContext = new SSLContextBuilder().loadTrustMaterial(null, (TrustStrategy) (chain, authType) -> true).build();
                SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslContext);
                return HttpClients.custom().setSSLSocketFactory(sslsf).build();
            } catch (Exception e) {
                throw new RuntimeException(e.getMessage(), e);
            }
        }
        return HttpClients.custom().build();
    }

    /**
     * 客户端下载
     *
     * @param url     下载地址
     * @param params  参数
     * @param headers header
     * @return 文件流
     */
    public static InputStream clientDownload(CloseableHttpClient httpClient, String url, Map<String, Object> params, Map<String, String> headers) {
        logger.info("请求地址:[" + url + "]参数:" + params);
        url = setParams(url, params);
        HttpGet httpGet = new HttpGet(url);
        addHeaders(httpGet, headers);
        return getResponseStream(url, UTF8, httpGet, httpClient);
    }

    /**
     * 客户端下载
     *
     * @param url    下载地址
     * @param params 参数
     * @return 文件流
     */
    public static InputStream clientDownload(CloseableHttpClient httpClient, String url, Map<String, Object> params) {
        return clientDownload(httpClient, url, params, null);
    }

    /**
     * 客户端下载
     *
     * @param url 下载地址
     * @return 文件流
     */
    public static InputStream clientDownload(CloseableHttpClient httpClient, String url) {
        return clientDownload(httpClient, url, null, null);
    }

    /**
     * 客户端下载
     *
     * @param url      下载地址
     * @param pathname 保存文件名(包含路径)
     * @param params   参数
     * @param headers  请求头
     */
    public static void clientSaveDownload(String url, String pathname, Map<String, Object> params, Map<String, String> headers) {
        File file = new File(pathname);

        if (!file.getParentFile().exists()) {
            file.getParentFile().mkdirs();
        }

        if (file.exists() && file.isFile()) {
            file.delete();
        }
        // 创建默认的httpClient实例
        CloseableHttpClient httpClient = createSSLClientDefault(url);
        try (InputStream is = clientDownload(httpClient, url, params, headers);
             FileOutputStream fos = new FileOutputStream(pathname)) {
            byte[] buffer = new byte[2048];
            int len;
            while ((len = is.read(buffer)) != -1) {
                fos.write(buffer, 0, len);
            }
            fos.flush();
        } catch (Exception e) {
            throw new RuntimeException(e.getMessage(), e);
        } finally {
            try {
                httpClient.close();
            } catch (Exception e) {
                throw new RuntimeException(e.getMessage(), e);
            }
        }
    }

    /**
     * 客户端下载
     *
     * @param url      下载地址
     * @param pathname 保存文件名(包含路径)
     * @param params   参数
     */
    public static void clientSaveDownload(String url, String pathname, Map<String, Object> params) {
        clientSaveDownload(url, pathname, params, null);
    }

    /**
     * 客户端下载
     *
     * @param url      下载地址
     * @param pathname 保存文件名(包含路径)
     */
    public static void clientSaveDownload(String url, String pathname) {
        clientSaveDownload(url, pathname, null, null);
    }

    /**
     * 客户端上传文件
     *
     * @param url       上传地址
     * @param is        本地文件流
     * @param filename  文件名
     * @param paramName 上传名称
     * @return 响应数据
     */
    public static String clientUploadFile(String url, InputStream is, String filename, String paramName) {
        HttpPost httpost = postForm(url, null);
        return clientUploadFile(url, is, filename, paramName, httpost);
    }

    /**
     * 客户端上传文件
     *
     * @param url       上传地址
     * @param is        本地文件流
     * @param filename  文件名
     * @param paramName 上传名称
     * @return 响应数据
     */
    public static String clientUploadFile(String url, InputStream is, String filename, String paramName, HttpPost httpPost) {
        CloseableHttpClient client = createSSLClientDefault(url);
        MultipartEntityBuilder builder = MultipartEntityBuilder.create();
        builder.setMode(HttpMultipartMode.BROWSER_COMPATIBLE);
        builder.addBinaryBody(paramName, is, ContentType.create("multipart/form-data"), filename);
        HttpEntity entity = builder.build();
        httpPost.setEntity(entity);
        return getResponseContent(url, UTF8, httpPost, client);
    }

    /**
     * 客户端上传文件
     *
     * @param url       上传地址
     * @param is        本地文件流
     * @param filename  文件名
     * @param paramName 上传名称
     * @param params    参数
     * @return 响应数据
     */
    public static String clientUploadFile(String url, InputStream is, String filename, String paramName, Map<String, Object> params) {
        HttpPost httpost = postForm(url, params);
        return clientUploadFile(url, is, filename, paramName, httpost);
    }


    /**
     * 客户端上传文件
     *
     * @param url       上传地址
     * @param is        本地文件流
     * @param filename  文件名
     * @param paramName 上传名称
     * @param params    参数
     * @param headers   header
     * @return 响应数据
     */
    public static String clientUploadFile(String url, InputStream is, String filename, String paramName, Map<String, Object> params, Map<String, String> headers) {
        HttpPost httpost = postForm(url, params);
        addHeaders(httpost, headers);
        return clientUploadFile(url, is, filename, paramName, httpost);
    }

    public static String postJson(String url, String jsonString) {
        return postJson(url, jsonString, null);
    }

    public static String postJson(String url, String jsonString, Map<String, String> headers) {
        logger.info("请求地址:[" + url + "]参数:" + jsonString);
        HttpPost request = new HttpPost(url);
        addHeaders(request, headers);
        //解决中文乱码问题
        StringEntity requestEntity = new StringEntity(jsonString, UTF8);
        requestEntity.setContentEncoding(UTF8);
        requestEntity.setContentType("application/json");
        request.setEntity(requestEntity);
        return getResponseContent(url, UTF8, request, createSSLClientDefault(url));
    }

    private static String ascii2native(String asciicode) {
        String[] asciis = asciicode.split("\\\\u");
        String nativeValue = asciis[0];
        try {
            for (int i = 1; i < asciis.length; i++) {
                String code = asciis[i];
                nativeValue += (char) Integer.parseInt(code.substring(0, 4), 16);
                if (code.length() > 4) {
                    nativeValue += code.substring(4, code.length());
                }
            }
        } catch (NumberFormatException e) {
            return asciicode;
        }
        return nativeValue;
    }


}
